package com.example.mycomponent.component

import android.util.Log
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastFirstOrNull
import androidx.compose.ui.zIndex
import kotlin.math.roundToInt

const val LOG_TAG = "DraggableLazyColumn"

data class MyItem(
    val id: Int,
    val name: String
)

/**
 * 拖动排序
 * 参考：https://github.com/aclassen/ComposeReorderable
 */
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun DraggableLazyColumn() {
    // A map to store the offset for each item
    var items by remember { mutableStateOf(
        listOf(
            MyItem(1, "Item 1"),
            MyItem(2, "Item 2"),
            MyItem(3, "Item 3"),
            MyItem(4, "Item 4"),
            MyItem(5, "Item 5")
        )
    ) }
    var draggingItemInfo by remember { mutableStateOf<LazyListItemInfo?>(null) }
    val listState = rememberLazyListState()
    var isMoved by remember { mutableStateOf(false) }

    LazyColumn(
        state = listState,
        modifier = Modifier
            .fillMaxSize()
            .padding(top = 16.dp)
    ) {
        items(items, key = { it.id }) { item ->
            // Get the index of the current item
            // Remember the offset for this item
            var offset by remember { mutableStateOf(Offset.Zero) }
            val animatedOffset by animateFloatAsState(targetValue = offset.y)
            val elevation = animateDpAsState(if (draggingItemInfo?.key == item.id) 8.dp else 0.dp)
            val shape = RoundedCornerShape(if (draggingItemInfo?.key == item.id) 0.dp else 8.dp)
            Box(
                modifier = Modifier
                    .shadow(elevation.value)
                    .padding(4.dp)
                    .offset { IntOffset(offset.x.roundToInt(), animatedOffset.roundToInt()) }
                    .fillMaxSize()
                    .background(color = MaterialTheme.colorScheme.primary, shape = shape)
                    .animateItemPlacement()
                    .pointerInput(Unit) {
                        detectDragGestures(
                            onDragStart = {
                                isMoved = false
                                draggingItemInfo = listState.layoutInfo.visibleItemsInfo.fastFirstOrNull { it.key == item.id }
                                Log.d(LOG_TAG, "current index is ${draggingItemInfo?.index}")
                            },
                            onDrag = { change, dragAmount ->
                                change.consume()
                                val height = listState.layoutInfo.visibleItemsInfo.first().size.toFloat()
                                offset += dragAmount

                                val index = draggingItemInfo?.index
                                val targetIndex = calculateTargetIndex(offset.y, index!!, items.size, height)
                                if (targetIndex != index && !isMoved) {
                                    items = items.toMutableList().apply {
                                        add(targetIndex, removeAt(index))
                                        offset = Offset.Zero
                                    }
                                    isMoved = true

                                    Log.d(LOG_TAG, "it's not equals, currentIndex is $index, targetIndex is $targetIndex")
                                }
                            },
                            onDragEnd = {
                                offset = Offset.Zero
                                draggingItemInfo = null
                            }
                        )
                    }
                    .zIndex(draggingItemInfo.let { if (it?.key == item.id) 1f else 0f })
            ) {
                // Content of each item
                Row {
                    Text(
                        text = item.name,
                        modifier = Modifier
                            .padding(16.dp)
                            .weight(1f)
                    )
                    Button(onClick = {
                    }) {
                        Text(text = "UP")
                    }
                }
            }
        }
    }
}

private fun calculateTargetIndex(offsetY: Float, currentIndex: Int, listSize: Int, height: Float): Int {
    var num = 1
    if (offsetY < 0f) {
        for (i in currentIndex-1 downTo 0) {
            val y = -offsetY
            val startOffsetY = height * num
            val endOffsetY = startOffsetY + height / 2
            if (y in startOffsetY..endOffsetY) {
                Log.d(LOG_TAG, "calculateTargetIndex: $i, startOffsetY: $startOffsetY, endOffsetY: $endOffsetY")
                return i
            }
            ++num
        }
    } else {
        for (i in currentIndex+1 until listSize) {
            val startOffsetY = height * num
            val endOffsetY = startOffsetY + height / 2
            if (offsetY in startOffsetY..endOffsetY) {
                return i
            }
            ++num
        }
    }

    return currentIndex
}
